#include "qelib.h"
#include "symbol_table.h"



#define SYMBOL_TAB_LOG_DOMAIN       "symtab"
#define sym_debug(...)              qelog_debug(SYMBOL_TAB_LOG_DOMAIN, __VA_ARGS__)
#define sym_info(...)               qelog_info(SYMBOL_TAB_LOG_DOMAIN, __VA_ARGS__)
#define sym_warning(...)            qelog_warning(SYMBOL_TAB_LOG_DOMAIN, __VA_ARGS__)
#define sym_error(...)              qelog_error(SYMBOL_TAB_LOG_DOMAIN, __VA_ARGS__)



qe_u32 symtab_create_entry(symbol_table *tab, qe_const_str name, 
    qe_u8 crc6, qe_u8 len, qe_u32 tag)
{
    qe_u8 *pb;

    /* check if symbol table full */
    if (tab->next_free + len + 4 >= tab->tabsize) {
        sym_error("symbol table full");
        return QE_NULL;
    }

    pb = tab->bytes;

    sym_debug("create entry name:%s crc6:%d len:%d tag:%d", 
        name, crc6, len, (qe_u16)tag);
    sym_debug("next free:%d pb:%p", tab->next_free, pb);

    /* set last entry index and tag */
    pb[tab->next_free]     = (qe_u8)(tab->hash[crc6] & 0xFF);
    pb[tab->next_free + 1] = (qe_u8)(tab->hash[crc6] / 0x100);
    pb[tab->next_free + 2] = (qe_u8)(tag & 0xFF);
    pb[tab->next_free + 3] = (qe_u8)(tag / 0x100);
    sym_debug("bytes[%d] = 0x%x", tab->next_free    , pb[tab->next_free    ]);
    sym_debug("bytes[%d] = 0x%x", tab->next_free + 1, pb[tab->next_free + 1]);
    sym_debug("bytes[%d] = 0x%x", tab->next_free + 2, pb[tab->next_free + 2]);
    sym_debug("bytes[%d] = 0x%x", tab->next_free + 3, pb[tab->next_free + 3]);

    /* set name */
    qe_strncpy((char *)&pb[tab->next_free + 4], name, len);
    sym_debug("copy %d to %p", len, &pb[tab->next_free + 4]);
    
    /* set zero termination */
    pb[tab->next_free + 4 + len] = '\0';

    /* store index of entry */
    tab->hash[crc6] = (qe_u16)tab->next_free;
    sym_debug("hash[%d] = %d(next free)", crc6, tab->hash[crc6]);

    /* update next free index */
    tab->next_free += len + 5;
    sym_debug("update next free:%d", tab->next_free);

    return (tab->next_free - (len + 5));
}

qe_u32 symtab_lookup_entry(symbol_table *tab, qe_const_str name, 
    qe_u8 crc6, qe_u8 len, qe_u32 tag)
{
    qe_u16 i = tab->hash[crc6];

    sym_debug("lookup entry name:%s crc6:%d len:%d tag:%d", 
        name, crc6, len, (qe_u16)tag);
    sym_debug("hash[%d]:%d", crc6, i);

    while (i != 0) {
        if (tab->bytes[i + 2] == (tag & 0x00FF)) {
            if (tab->bytes[i + 3] == (tag / 0x100)) {
                if (tab->bytes[i + 4 + len] == '\0') {
                    if (qe_strncmp((char*)(&tab->bytes[i + 4]), name, len) == 0) {
                        break;
                    }
                }
            }
        }
        i = (qe_u16)(tab->bytes[i] + (tab->bytes[i + 1] * 0x100));
        sym_debug("update i:%d", i);
    }
    sym_debug("return i:%d", i);
    return i;
}

static void get_checksum(qe_const_str name, qe_u8 *pcrc, qe_u8 *plen)
{
	unsigned char c;
	int length = 1;		/* Should be 1 to account for '\0' */
	int crc = 0;

	if (name != (const char *) 0)
	{
		for (; (c = (unsigned char) *name++) != '\0';)
		{
			crc += c;
			length++;
		}
	}
	*pcrc = (qe_u8)(crc & 0x3F);
	*plen = (qe_u8)length;
}

qe_u32 symbol_open(symbol_table *tab, qe_const_str name, qe_u32 tag)
{
	qe_u16 result;
	qe_u8 len;
	qe_u8 crc;

	len = 0;
	crc = 0;

    get_checksum(name, &crc, &len);
    sym_debug("crc:%d len:%d", crc, len);

    result = symtab_lookup_entry(tab, name, crc, len, tag);
    if (!result) {
        sym_debug("%s not found", name);
        result = symtab_create_entry(tab, name, crc, len, tag);
        sym_debug("create entry %d for %s", result, name);
    }

    return result;
}

void symbol_tab_init(symbol_table *tab, qe_size size)
{
    qe_memset(tab, 0, sizeof(symbol_table));
    tab->tabsize = size;
    tab->next_free = 1;
    tab->bytes = qe_malloc(size);
    qe_memset(tab->bytes, 0, size);
    sym_debug("bytes addr %p", tab->bytes);
}